home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 003 / multidb.arc / MULTIDB.TXT
Encoding:
Text File  |  1985-11-01  |  14.9 KB  |  475 lines

  1. October 25, 1985
  2.  
  3. TO:   All interested dBASE and Clipper users 
  4.  
  5. FROM: K. E. Saffer
  6.  
  7. RE:   Multi-user file access
  8.  
  9.  
  10.      It all started in 1983 while I was writing a financial 
  11. application for an interior design firm in Springfield, MA.  I 
  12. was using dBASE II on a Columbia MPC running MP/M-68.  We had 8 
  13. users on line and I had to come up with some sort of file access 
  14. control methods or the users would most certainly collide with 
  15. each other sooner or later.
  16.  
  17.      Being experienced with LAN systems I possessed a working 
  18. knowledge of how they control file access.  Either on disk or 
  19. located somewhere in shared memory is a structure called a 
  20. semaphore table.  Each record within this structure has space for 
  21. a data file name and in the case of record locking schemes a 
  22. record number.  There is also other specialized information that 
  23. varies from system to system, but we are only interested in the 
  24. basics.
  25.  
  26.      When a user application wishes to open a file in a 
  27. read/write mode, it would first check the semaphore table to 
  28. determine if the file and record were free to use.  If so, the 
  29. semaphore table was rapidly updated to show that the file was in 
  30. use (locked).  The changes were made to the file, any dirty 
  31. buffers were flushed, and the semaphore table was again updated 
  32. to show that the file was now free to be used (unlocked).  
  33.  
  34.      Before we progress any further, some facts about dBASE data 
  35. file access must be known.  When you USE a data file, you are in 
  36. reality opening the file in read-write mode.  So long as you do 
  37. not use any commands that make changes to a data file (EDIT, 
  38. REINDEX, and the dreaded APPEND BLANK to name a few) the file may 
  39. be closed without writing any information the the disk drive.  
  40. This allows multiple users to scan and search though the file all 
  41. day long without doing any damage.
  42.                 
  43.      Also, the actual amount of data brought into memory from a 
  44. file read is rarely a single record.  In reality, several records 
  45. are brought into memory, so say bye-bye to any hope of using 
  46. record locking techniques in this system (or at least until AT 
  47. comes out with a workable multi-user database manager).
  48.  
  49.      This leaves us with file locking as our only recourse.  As 
  50. the name implies, once a file is locked by an application, no 
  51. other application can make changes to the file until the original 
  52. application is through with it.  This requires that an 
  53. application store the changes to be made in local memory 
  54. variables, then quickly open the file, perform any REPLACEs and 
  55. close it.  If you have been in the habit of allowing a user to 
  56. keep a file open in USE all the time and are performing @..GETs 
  57. on the data file fields directly, you will have to make changes 
  58. in your programming style.
  59.  
  60.      I decided to implement a semaphore table on disk as a dBASE 
  61. database.  At first a single database was used to hold all 
  62. references to locked files, but one day that file became 
  63. corrupted due to two users trying to lock two different files at 
  64. the same time.  To correct this, I use a small database file as a 
  65. semaphore table for each database file that requires controlled 
  66. access.
  67.  
  68.      Enough of this! How do I use it ?!?!?!? 
  69.  
  70.      OK, OK. I know this is boring, but your multi-user users are 
  71. gonna love you for this.  Here is a real-life example.  We have a 
  72. dBASE file called CUSTOMER.DBF, with it's associated index 
  73. CUSTOMER.NDX.  To provide control over the customer file we will 
  74. create a semaphore file called CUSTLOCK.DBF.  Its structure 
  75. consists of one logical field called LOCKED.  The CUSTLOCK data 
  76. file will have only one record in it, and the LOCKED field will 
  77. be initialized to .T.
  78.  
  79.      The program that manipulates the customer file has three 
  80. separate sections in it: viewing, editing, and adding records.
  81. When the application is viewing the file, the semaphore file is 
  82. ignored because the view section will never make any changes to 
  83. it.  When editing or adding records, the program will first check 
  84. the semaphore file to make sure no one else is making changes.  
  85. If the file is locked, the operator is prompted to wait until 
  86. it becomes available, otherwise the file is locked and changes 
  87. made.  Then the program will close the customer file, update the 
  88. semaphore file and proceed about it's business.
  89.  
  90.      The trick here is to read the data file information into 
  91. local memory variables, perform editing on these, then REPLACE 
  92. the data file fields with the memory variables.  This gives the 
  93. program the opportunity of knowing exactly when it is going to 
  94. make any changes to the file.  We shall maintain control.
  95. Make darn sure your program never changes a file without your 
  96. permission, or you will most certainly corrupt the file and 
  97. possibly lose your job.
  98.  
  99.      The following is an extract of a customer data file 
  100. manipulation program I wrote for an advanced point-of-sale 
  101. application used by a local retailer.  The system is compiled 
  102. using Clipper, but all Clipper-specific code has been removed or 
  103. rewritten to allow operation under dBASE III.  Remember that in 
  104. addition to the data manipulation programs, you must provide a 
  105. program that will unlock all semaphore files in case the system 
  106. is accidentally interrupted (i.e. power failure, lamebrain user, 
  107. etc.)
  108.  
  109.  
  110.  
  111. The structures of the files follow:
  112.  
  113. File CUSTOMER.DBF
  114. Number of records:   30
  115. Fieldname  Type Length Decimals
  116. ---------- ---- ------ --------
  117. FIRST_NAME  C       20        0
  118. INITIAL     C        2        0
  119. LAST_NAME   C       20        0
  120. ADDRESS1    C       40        0
  121. ADDRESS2    C       40        0
  122. ADDRESS3    C       40        0
  123. ZIP_CODE    C       10        0
  124.  
  125. File Customer is indexed on UPPER(LAST_NAME) to CUSTOMER.NDX.
  126.  
  127.  
  128. File CUSTLOCK.DBF
  129. Number of records:    1
  130. Fieldname  Type Length Decimals
  131. ---------- ---- ------ --------
  132. LOCKED      L        1        0
  133.  
  134.  
  135. The file manipulation program follows:
  136.  
  137. * Program ------:CUSTMAIN.PRG
  138. * Author -------:Kevin E. Saffer
  139. * Date ---------:October 28, 1985   
  140. * Notes --------:Demonstrate methods for multi-users file access control
  141. *
  142. *    This program has been freely placed into the public domain without
  143. *    any restrictions concerning its use.
  144.  
  145. * establish working environment
  146. SET BELL OFF
  147. SET COLOR TO +7/0,7/0,0
  148. SET DELETED ON
  149. SET DELIMITER ON
  150. SET DELIMITER TO '[]'
  151. SET EXACT OFF
  152. SET HEADINGS OFF
  153. SET SAFETY OFF
  154. SET TALK OFF
  155.  
  156. * Open the customer file and display heading
  157. SELECT A
  158. USE Customer.dbf INDEX Customer.ndx
  159. CLEAR
  160. @ 01,00 SAY "Customer Maintenance"
  161. @ 01,67 SAY "Date " + DTOC(DATE())
  162. @ 02,00 SAY "========================================" + ;
  163.             "========================================"
  164. * start main loop
  165. DO WHILE .T. 
  166.  
  167.   SET COLOR TO 7/0,+7/0
  168.  
  169.   * read file fields into local memvars
  170.   Mfirst = First_name
  171.   Minit = Initial
  172.   Mlast = Last_name
  173.   Maddr1 = Address1
  174.   Maddr2 = Address2
  175.   Maddr3 = Address3
  176.   Mzip = Zip_code
  177.  
  178.   * get the record number for use later
  179.   Mrec_no = RECNO()
  180.  
  181.   * display memvars
  182.   @ 04,00 SAY " Lastname" GET Mlast 
  183.   @ 04,59 SAY "Record number [" + STR(Mrec_no,5,0) + "]"
  184.   @ 05,00 SAY "Firstname" GET Mfirst
  185.   @ 06,00 SAY "  Initial" GET Minit
  186.   @ 08,00 SAY "  Address" GET Maddr1
  187.   @ 09,00 SAY "         " GET Maddr2
  188.   @ 10,00 SAY "         " GET Maddr3
  189.   @ 11,00 SAY " Zip Code" GET Mzip 
  190.   CLEAR GETS
  191.  
  192.   * prompt operator
  193.   Choice = " "
  194.   SET CONFIRM OFF
  195.   DO WHILE AT(Choice,"PNAEDX") = 0
  196.     Choice = " "
  197.     @ 23,00
  198.     @ 23,00 SAY "P)revious, N)ext, A)dd, E)dit, D)elete or eXit?" ;
  199.     GET Choice PICTURE '!'
  200.     READ
  201.   ENDDO
  202.  
  203.   DO CASE
  204.   CASE Choice = "X"
  205.     * exit program
  206.     CLEAR
  207.     QUIT
  208.  
  209.   CASE Choice = "P"
  210.     * previous record
  211.     SKIP -1
  212.     IF BOF()
  213.       @ 23,00
  214.       @ 23,00 SAY "Beginning of file encountered, going to bottom..."
  215.       GO BOTTOM
  216.       LOOP
  217.     ELSE
  218.       LOOP
  219.     ENDIF
  220.  
  221.   CASE Choice = "N"
  222.  
  223.     * next record
  224.     SKIP 
  225.     IF EOF()
  226.       @ 23,00
  227.       @ 23,00 SAY "End of file encountered, going to top..."
  228.       GO TOP 
  229.       LOOP    
  230.     ELSE
  231.       LOOP
  232.     ENDIF
  233.  
  234.   CASE Choice = "A"
  235.     * add a new record
  236.  
  237.     * first, initialize memvars for operator input
  238.     Mlast = "                    "
  239.     Mfirst = "                    "
  240.     Minit = "  "
  241.     Maddr1 = "                                        "
  242.     Maddr2 = "                                        "
  243.     Maddr3 = "                                        "
  244.     Mzip = "          "
  245.  
  246.     * allow operator to edit the memvars
  247.     Edit_more = " "
  248.     DO WHILE Edit_more <> "Y"
  249.       Edit_more = " "
  250.       @ 04,00 SAY " Lastname" GET Mlast 
  251.       @ 05,00 SAY "Firstname" GET Mfirst
  252.       @ 06,00 SAY "  Initial" GET Minit
  253.       @ 08,00 SAY "  Address" GET Maddr1
  254.       @ 09,00 SAY "         " GET Maddr2
  255.       @ 10,00 SAY "         " GET Maddr3
  256.       @ 11,00 SAY " Zip Code" GET Mzip
  257.       @ 23,00
  258.       @ 23,00 SAY "Please enter the customer information."
  259.       READ
  260.       @ 23,00
  261.       @ 23,00 SAY "Is the above information correct? (Y/N)" ;
  262.       GET Edit_more PICTURE '!'
  263.       READ
  264.     ENDDO
  265.  
  266.     * prompt operator to add or exit
  267.     Add_it = " "
  268.     @ 23,00
  269.     @ 23,00 SAY "A)dd this new record or E)xit?" GET Add_it PICTURE '!'
  270.     READ
  271.     IF Add_it <> "A"
  272.       * go back to display routine
  273.       LOOP
  274.     ENDIF
  275.  
  276.     * lock up customer file, we are still in work area A
  277.     @ 23,00 
  278.  
  279.     @ 23,00 SAY "Adding new record..."
  280.  
  281.     USE Custlock.dbf
  282.     IF Locked
  283.       SET COLOR TO *15/0,+7/0
  284.       @ 23,00
  285.       @ 23,00 SAY "Customer file in use, retrying..."
  286.       SET COLOR TO 7/0,+7/0
  287.     ENDIF
  288.     DO WHILE Locked
  289.       USE
  290.       USE Custlock.dbf
  291.     ENDDO
  292.     REPLACE Locked WITH .T.
  293.     USE
  294.     
  295.     * the semaphore database now shows the customer file in use, perform
  296.     * replacements
  297.  
  298.     USE Customer.dbf INDEX Customer.ndx
  299.     APPEND BLANK
  300.     REPLACE First_name WITH Mfirst
  301.     REPLACE Last_name WITH Mlast
  302.     REPLACE Initial WITH Minit
  303.     REPLACE Address1 WITH Maddr1
  304.     REPLACE Address2 WITH Maddr2
  305.     REPLACE Address3 WITH Maddr3
  306.     REPLACE Zip_code WITH Mzip
  307.     Mrec_no = RECNO()
  308.     USE
  309.  
  310.     * now, unlock the customer file and get back to the record we just added
  311.     USE Custlock.dbf
  312.     REPLACE Locked WITH .F.
  313.     USE Customer.dbf INDEX Customer.ndx
  314.     GO Mrec_no
  315.     LOOP
  316.  
  317.   CASE Choice = "E"
  318.     * editing the current record is just like adding, without initializing
  319.     * the memvars
  320.  
  321.     * allow operator to edit the memvars
  322.     Edit_more = " "
  323.     DO WHILE Edit_more <> "Y"
  324.       Edit_more = " "
  325.       @ 04,00 SAY " Lastname" GET Mlast 
  326.       @ 05,00 SAY "Firstname" GET Mfirst
  327.       @ 06,00 SAY "  Initial" GET Minit
  328.       @ 08,00 SAY "  Address" GET Maddr1
  329.       @ 09,00 SAY "         " GET Maddr2
  330.       @ 10,00 SAY "         " GET Maddr3
  331.       @ 11,00 SAY " Zip Code" GET Mzip
  332.       @ 23,00
  333.       @ 23,00 SAY "Please enter any corrections."
  334.  
  335.       READ
  336.       @ 23,00
  337.       @ 23,00 SAY "Is the above information correct? (Y/N)" ;
  338.       GET Edit_more PICTURE '!'
  339.       READ
  340.     ENDDO
  341.  
  342.     * prompt operator to add or exit
  343.     Add_it = " "
  344.     @ 23,00
  345.     @ 23,00 SAY "A)dd these changes or E)xit?" GET Add_it PICTURE '!'
  346.     READ
  347.     IF Add_it <> "A"
  348.       * go back to display routine
  349.       LOOP
  350.     ENDIF
  351.  
  352.     * lock up customer file, we are still in work area A
  353.     @ 23,00 
  354.     @ 23,00 SAY "Adding these changes..."
  355.     USE Custlock.dbf
  356.     IF Locked
  357.       SET COLOR TO *15/0,+7/0
  358.       @ 23,00 SAY "                                           "
  359.       @ 23,00 SAY "Customer file in use, retrying..."
  360.       SET COLOR TO 7/0,+7/0
  361.     ENDIF
  362.     DO WHILE Locked
  363.       USE
  364.       USE Custlock.dbf
  365.     ENDDO
  366.     REPLACE Locked WITH .T.
  367.     USE
  368.     
  369.     * the semaphore database now shows the customer file in use, perform
  370.     * replacements
  371.     USE Customer.dbf INDEX Customer.ndx
  372.  
  373.     * get back to the record we want
  374.     GO Mrec_no
  375.     REPLACE First_name WITH Mfirst
  376.     REPLACE Last_name WITH Mlast
  377.     REPLACE Initial WITH Minit
  378.     REPLACE Address1 WITH Maddr1
  379.     REPLACE Address2 WITH Maddr2
  380.     REPLACE Address3 WITH Maddr3
  381.     REPLACE Zip_code WITH Mzip
  382.     Mrec_no = RECNO()
  383.     USE
  384.  
  385.     * now, unlock the customer file and get back to the record we just edited
  386.     USE Custlock.dbf
  387.     REPLACE Locked WITH .F.
  388.     USE Customer.dbf INDEX Customer.ndx
  389.     GO Mrec_no
  390.  
  391.     LOOP
  392.  
  393.   CASE Choice = "D"
  394.     * delete record
  395.     Kill_it = " "
  396.     @ 23,00
  397.     @ 23,01 SAY "ARE YOU SURE YOU WANT THIS CUSTOMER DELETED? (Y/N)" ;
  398.     GET Kill_it PICTURE '!'
  399.     READ
  400.     IF Kill_it = "Y"
  401.       @ 23,00
  402.       @ 23,01 SAY "Deleting this customer..."
  403.       Oldrec = RECNO()
  404.       USE Custlock.dbf
  405.       IF Locked
  406.         SET COLOR TO *15/0,+7/0
  407.         @ 23,00
  408.         @ 23,01 SAY "Customer file in use, retrying..."
  409.         SET COLOR TO 7/0,+7/0
  410.       ENDIF
  411.       DO WHILE Locked
  412.         USE
  413.         USE Custlock.dbf
  414.       ENDDO
  415.       REPLACE Locked WITH .T.
  416.       USE
  417.       USE Customer.dbf INDEX Customer.ndx
  418.       GO Mrec_no
  419.  
  420.       * check to make sure no one else has deleted this record while we were 
  421.       * locking up the file
  422.       IF Last_name = Mlast .AND. First_name = Mfirst
  423.         DELETE
  424.         USE  
  425.         USE Custlock.dbf
  426.         REPLACE Locked WITH .F.
  427.         USE
  428.       ENDIF
  429.       USE Customer.dbf INDEX Customer.ndx
  430.       GO TOP      
  431.       @ 23,00 CLEAR
  432.     ENDIF
  433.   ENDCASE 
  434. ENDDO
  435.  
  436. * end of file CUSTMAIN.PRG
  437.  
  438.  
  439.  
  440.      The actual program in use has search and print capabilities, 
  441. a function to unlock the semaphore file, and an indexing routine.  
  442. The indexing routine is important because the program does not 
  443. pack the data file when a record is deleted.  The operator thinks 
  444. it's gone because we SET DELETED ON in the beginning of the 
  445. program.  Therefore, once a week the file is re-indexed to remove 
  446.  
  447. any deleted records.
  448.  
  449.      The program runs relatively slowly in interpetive dBASE, but 
  450. thats what the Clipper compiler is for.  The compiled application 
  451. goes like lightning.
  452.  
  453.      The advantage to having the program take care of the file 
  454. locking methods is that now the application is not "LAN 
  455. specific".  It can run on nearly any LAN or multi-user system, so 
  456. long as the system manager give everyone read-write access to the 
  457. volume where the semaphore files reside.
  458.  
  459.      While you are experimenting with the program, see what 
  460. happens when you try to edit the file when the semaphore database 
  461. shows the file is locked.  Currently, I make the user wait a 
  462. second or two until the file becomes available.  You might give 
  463. the operator a choice of whether to wait for the file, or exit.
  464.  
  465.      The program by itself is only a demonstration of how 
  466. controlled file sharing may be accomplished and is nowhere near a 
  467. complete database manipulation program.  I leave it up to you to 
  468. make it perfect.
  469.  
  470.  
  471. Thank you for your support.
  472.  
  473. KES
  474.  
  475.